#ifndef __C_NGISDATA_UDXSCHEMA_DATASET_H__
#define __C_NGISDATA_UDXSCHEMA_DATASET_H__

#include "IUdxSchemaDataset.h"
#include "UdxNodeSchema.h"
#include "tinyxml2.h"

namespace NGIS
{
	namespace Data
	{
		namespace Schema
		{
			class CUdxDatasetSchema: public virtual IUdxDatasetSchema, public virtual CUdxNodeSchema
			{
			public:
				CUdxDatasetSchema(const char* pName) : CUdxNodeSchema(NULL, pName, NULL)
				{
					mDescription = new CUdxSchemaDescription(EDTKT_NODE);
				}

				~CUdxDatasetSchema() 
				{ }

				void ParseXDO(IUdxNodeSchema* containerNode, tinyxml2::XMLElement* element)
				{
					std::string name = element->Attribute("name");
					std::string description = element->Attribute("description");
					std::string typeStr = element->Attribute("type");
					ESchemaNodeType kernelType;
					if (typeStr == "external")
					{
						kernelType = ESchemaNodeType::EDTKT_NODE;

						IUdxNodeSchema* node = containerNode->addChildNode(name.c_str(), kernelType, description.c_str());
						std::string resourceId = element->Attribute("externalId");
						node->getDescription()->modifyDataTemplateTag(resourceId.c_str());
					}
					else
					{
						kernelType = String2SchemaNodeType(typeStr.c_str());

						IUdxNodeSchema* node = containerNode->addChildNode(name.c_str(), kernelType, description.c_str());

						if (kernelType == ESchemaNodeType::EDTKT_NODE ||
							kernelType == ESchemaNodeType::EDTKT_LIST ||
							kernelType == ESchemaNodeType::EDTKT_MAP ||
							kernelType == ESchemaNodeType::EDTKT_TABLE)
						{
							for (tinyxml2::XMLElement* childEle = element->FirstChildElement(); childEle; childEle = childEle->NextSiblingElement())
							{
								ParseXDO(node, childEle);
							}
						}
					}
				}

				void FormatXDO(IUdxNodeSchema* pNode, tinyxml2::XMLElement* element, tinyxml2::XMLElement* rootElement)
				{
					ESchemaNodeType kernelType = pNode->getDescription()->getKernelType();
					const char* name = pNode->getName();
					const char* nodeType = SchemaNodeType2String(kernelType);
					const char* nodeInfo = pNode->getDescription()->getNodeDescription();
					tinyxml2::XMLDocument* doc = element->GetDocument();
					tinyxml2::XMLElement* childEle = doc->NewElement("UdxNode");
					element->LinkEndChild(childEle);
					childEle->SetAttribute("name", name);
					childEle->SetAttribute("type", nodeType);
					childEle->SetAttribute("description", nodeInfo);

					tinyxml2::XMLElement* semanticNode = rootElement->FirstChildElement("SemanticAttachment");
					tinyxml2::XMLElement* conceptsEle = NULL;
					tinyxml2::XMLElement* spatialRefsEle = NULL;
					tinyxml2::XMLElement* unitsEle = NULL;
					tinyxml2::XMLElement* dataTemplatesEle = NULL;
					if (semanticNode==NULL)
					{
						semanticNode = doc->NewElement("SemanticAttachment");
						rootElement->LinkEndChild(semanticNode);

						conceptsEle = doc->NewElement("Concepts");
						semanticNode->LinkEndChild(conceptsEle);
						spatialRefsEle = doc->NewElement("SpatialRefs");
						semanticNode->LinkEndChild(spatialRefsEle);
						unitsEle = doc->NewElement("Units");
						semanticNode->LinkEndChild(unitsEle);
						dataTemplatesEle = doc->NewElement("DataTemplates");
						semanticNode->LinkEndChild(dataTemplatesEle);
					}
					else
					{
						conceptsEle = semanticNode->FirstChildElement("Concepts");
						spatialRefsEle = semanticNode->FirstChildElement("SpatialRefs");
						unitsEle = semanticNode->FirstChildElement("Units");
						dataTemplatesEle = semanticNode->FirstChildElement("DataTemplates");
						if (conceptsEle==NULL)
						{
							conceptsEle = doc->NewElement("Concepts");
							semanticNode->LinkEndChild(conceptsEle);
						}
						if (spatialRefsEle==NULL)
						{
							spatialRefsEle = doc->NewElement("SpatialRefs");
							semanticNode->LinkEndChild(spatialRefsEle);
						}
						if (unitsEle==NULL)
						{
							unitsEle = doc->NewElement("Units");
							semanticNode->LinkEndChild(unitsEle);
						}
						if (dataTemplatesEle==NULL)
						{
							dataTemplatesEle = doc->NewElement("DataTemplates");
							semanticNode->LinkEndChild(dataTemplatesEle);
						}
					}
					std::string conceptInfo = pNode->getDescription()->getConceptTag();
					std::string spatialRefInfo = pNode->getDescription()->getSpatialReferencefTag();
					std::string unitInfo = pNode->getDescription()->getUnitTag();
					std::string dataTemplateInfo = pNode->getDescription()->getDataTemplateTag();
					if (conceptInfo !="")
					{
						tinyxml2::XMLElement* s_node = doc->NewElement("Concept");
						conceptsEle->LinkEndChild(s_node);
						s_node->SetAttribute("nodeId", name);
						s_node->SetAttribute("conceptId", conceptInfo.c_str());
					}
					if (spatialRefInfo!="")
					{
						tinyxml2::XMLElement* s_node = doc->NewElement("SpatialRef");
						spatialRefsEle->LinkEndChild(s_node);
						s_node->SetAttribute("nodeId", name);
						s_node->SetAttribute("spatialRefId", spatialRefInfo.c_str());
					}
					if (unitInfo!="")
					{
						tinyxml2::XMLElement* s_node = doc->NewElement("Unit");
						unitsEle->LinkEndChild(s_node);
						s_node->SetAttribute("nodeId", name);
						s_node->SetAttribute("unitId", unitInfo.c_str());
					}
					if (dataTemplateInfo!="")
					{
						tinyxml2::XMLElement* s_node = doc->NewElement("DataTemplate");
						dataTemplatesEle->LinkEndChild(s_node);
						s_node->SetAttribute("nodeId", name);
						s_node->SetAttribute("dataTemplateId", dataTemplateInfo.c_str());
					}

					if (kernelType == ESchemaNodeType::EDTKT_NODE ||
						kernelType == ESchemaNodeType::EDTKT_LIST || 
						kernelType == ESchemaNodeType::EDTKT_MAP ||
						kernelType == ESchemaNodeType::EDTKT_TABLE)
					{
						int count = pNode->getChildNodeCount();
						for (int iNode=0; iNode<count; iNode++)
						{
							IUdxNodeSchema* tempNode = pNode->getChildNode(iNode);
							FormatXDO(tempNode, childEle, rootElement);
						}
					}
				}

				IUdxNodeSchema* getNodeWithId(std::string pNodeId, IUdxNodeSchema* pParentNode)
				{
					int count = pParentNode->getChildNodeCount();
					for (int iNode=0; iNode<count; iNode++)
					{
						IUdxNodeSchema* tempNode = pParentNode->getChildNode(iNode);
						std::string temp_name = tempNode->getName();
						if (temp_name == pNodeId)
						{
							return tempNode;
						}
						else
						{
							tempNode = getNodeWithId(pNodeId, tempNode);
							if (tempNode) return tempNode;
						}
					}
					return NULL;
				}
			public:
				virtual const char* getName() { return CUdxNodeSchema::getName(); }

				virtual INodeDescription* getDescription() { return CUdxNodeSchema::getDescription(); }

				virtual bool modifyName(const char* pName) { return CUdxNodeSchema::modifyName(pName); }

				virtual IUdxNodeSchema* getParentNode() { return NULL; }

				virtual int getChildNodeCount() { return CUdxNodeSchema::getChildNodeCount(); }

				virtual IUdxNodeSchema* getChildNode(int idx) { return CUdxNodeSchema::getChildNode(idx); }

				virtual IUdxNodeSchema* addChildNode(const char* pName, INodeDescription* pDescription) { return CUdxNodeSchema::addChildNode(pName, pDescription); }

				virtual IUdxNodeSchema* addChildNode(const char* pName, int pNodeType, const char* pNodeInfo="") { return CUdxNodeSchema::addChildNode(pName, pNodeType, pNodeInfo); }

				virtual bool removeChildNode(IUdxNodeSchema* pNode) { return CUdxNodeSchema::removeChildNode(pNode); }

				virtual bool removeChildNode(int idx) { return CUdxNodeSchema::removeChildNode(idx); }

				virtual void setExtension(int flag) {}

				virtual int getExtension() { return 0; }

				virtual bool compareOther(IUdxNodeSchema* pNode) { return CUdxNodeSchema::compareOther(pNode); }

			public:
				virtual bool LoadFromXmlFile(const char* fileName)
				{
					tinyxml2::XMLDocument doc;
					doc.LoadFile( fileName );
					tinyxml2::XMLElement* rootEle = doc.RootElement();
					std::string dataset_name = rootEle->Attribute("name");
					std::string dataset_description = rootEle->Attribute("description");
					this->mName = dataset_name;
					this->getDescription()->modifyNodeDescription(dataset_description.c_str());

					rootEle = rootEle->FirstChildElement();

					for (tinyxml2::XMLElement* ele=rootEle->FirstChildElement(); ele; ele=ele->NextSiblingElement())
					{
						ParseXDO((CUdxNodeSchema*)this, ele);
					}

					tinyxml2::XMLElement* semanticEle = doc.RootElement()->FirstChildElement("SemanticAttachment");
					if (semanticEle!=NULL)
					{
						for (tinyxml2::XMLElement* childEle = semanticEle->FirstChildElement(); childEle; childEle = childEle->NextSiblingElement())
						{
							std::string ele_name = childEle->Name();
							if (ele_name=="Concepts")
							{
								for (tinyxml2::XMLElement* infoEle = childEle->FirstChildElement(); infoEle; infoEle = infoEle->NextSiblingElement())
								{
									std::string nodeId = infoEle->Attribute("nodeId");
									std::string resourceId = infoEle->Attribute("conceptId");
									IUdxNodeSchema* dataNode = getNodeWithId(nodeId, (CUdxNodeSchema*)this);
									if (dataNode)
										dataNode->getDescription()->modifyConceptTag(resourceId.c_str());
								}
							}
							else if (ele_name == "SpatialRefs")
							{
								for (tinyxml2::XMLElement* infoEle = childEle->FirstChildElement(); infoEle; infoEle = infoEle->NextSiblingElement())
								{
									std::string nodeId = infoEle->Attribute("nodeId");
									std::string resourceId = infoEle->Attribute("spatialRefId");
									IUdxNodeSchema* dataNode = getNodeWithId(nodeId, (CUdxNodeSchema*)this);
									if (dataNode)
										dataNode->getDescription()->modifySpatialReferenceTag(resourceId.c_str());
								}
							}
							else if (ele_name == "Units")
							{
								for (tinyxml2::XMLElement* infoEle = childEle->FirstChildElement(); infoEle; infoEle = infoEle->NextSiblingElement())
								{
									std::string nodeId = infoEle->Attribute("nodeId");
									std::string resourceId = infoEle->Attribute("unitId");
									IUdxNodeSchema* dataNode = getNodeWithId(nodeId, (CUdxNodeSchema*)this);
									if (dataNode)
										dataNode->getDescription()->modifyUnitTag(resourceId.c_str());
								}
							}
							else if (ele_name == "DataTemplates")
							{
								for (tinyxml2::XMLElement* infoEle = childEle->FirstChildElement(); infoEle; infoEle = infoEle->NextSiblingElement())
								{
									std::string nodeId = infoEle->Attribute("nodeId");
									std::string resourceId = infoEle->Attribute("dataTemplateId");
									IUdxNodeSchema* dataNode = getNodeWithId(nodeId, (CUdxNodeSchema*)this);
									if (dataNode)
										dataNode->getDescription()->modifyDataTemplateTag(resourceId.c_str());
								}
							}
						}
					}
					return true;
				}

				virtual bool FormatToXmlFile(const char* fileName)
				{
					tinyxml2::XMLDocument doc;
					tinyxml2::XMLElement* element = doc.NewElement("UdxDeclaration");
					doc.LinkEndChild(element);
					element->SetAttribute("name", this->getName());
					element->SetAttribute("description", this->getDescription()->getNodeDescription());

					tinyxml2::XMLElement* rootEle = doc.NewElement("UdxNode");
					element->LinkEndChild(rootEle);

					int count = this->getChildNodeCount();
					for (int iNode=0; iNode<count; iNode++)
					{
						IUdxNodeSchema* tempNode = this->getChildNode(iNode);
						FormatXDO(tempNode, rootEle, element);
					}

					doc.SaveFile(fileName);

					return true;
				}

				virtual bool LoadFromXmlStream(const char* xmlStr)
				{
					tinyxml2::XMLDocument doc;
					doc.Parse( xmlStr );
					tinyxml2::XMLElement* rootEle = doc.RootElement();
					std::string dataset_name = rootEle->Attribute("name");
					std::string dataset_description = rootEle->Attribute("description");
					this->mName = dataset_name;
					this->getDescription()->modifyNodeDescription(dataset_description.c_str());

					rootEle = rootEle->FirstChildElement();

					for (tinyxml2::XMLElement* ele=rootEle->FirstChildElement(); ele; ele=ele->NextSiblingElement())
					{
						ParseXDO((CUdxNodeSchema*)this, ele);
					}

					tinyxml2::XMLElement* semanticEle = doc.RootElement()->FirstChildElement("SemanticAttachment");
					if (semanticEle!=NULL)
					{
						for (tinyxml2::XMLElement* childEle = semanticEle->FirstChildElement(); childEle; childEle = childEle->NextSiblingElement())
						{
							std::string ele_name = childEle->Name();
							if (ele_name=="Concepts")
							{
								for (tinyxml2::XMLElement* infoEle = childEle->FirstChildElement(); infoEle; infoEle = infoEle->NextSiblingElement())
								{
									std::string nodeId = infoEle->Attribute("nodeId");
									std::string resourceId = infoEle->Attribute("conceptId");
									IUdxNodeSchema* dataNode = getNodeWithId(nodeId, (CUdxNodeSchema*)this);
									if (dataNode)
										dataNode->getDescription()->modifyConceptTag(resourceId.c_str());
								}
							}
							else if (ele_name == "SpatialRefs")
							{
								for (tinyxml2::XMLElement* infoEle = childEle->FirstChildElement(); infoEle; infoEle = infoEle->NextSiblingElement())
								{
									std::string nodeId = infoEle->Attribute("nodeId");
									std::string resourceId = infoEle->Attribute("spatialRefId");
									IUdxNodeSchema* dataNode = getNodeWithId(nodeId, (CUdxNodeSchema*)this);
									if (dataNode)
										dataNode->getDescription()->modifySpatialReferenceTag(resourceId.c_str());
								}
							}
							else if (ele_name == "Units")
							{
								for (tinyxml2::XMLElement* infoEle = childEle->FirstChildElement(); infoEle; infoEle = infoEle->NextSiblingElement())
								{
									std::string nodeId = infoEle->Attribute("nodeId");
									std::string resourceId = infoEle->Attribute("unitId");
									IUdxNodeSchema* dataNode = getNodeWithId(nodeId, (CUdxNodeSchema*)this);
									if (dataNode)
										dataNode->getDescription()->modifyUnitTag(resourceId.c_str());
								}
							}
							else if (ele_name == "DataTemplates")
							{
								for (tinyxml2::XMLElement* infoEle = childEle->FirstChildElement(); infoEle; infoEle = infoEle->NextSiblingElement())
								{
									std::string nodeId = infoEle->Attribute("nodeId");
									std::string resourceId = infoEle->Attribute("dataTemplateId");
									IUdxNodeSchema* dataNode = getNodeWithId(nodeId, (CUdxNodeSchema*)this);
									if (dataNode)
										dataNode->getDescription()->modifyDataTemplateTag(resourceId.c_str());
								}
							}
						}
					}
					
					return true;
				}

				virtual bool FormatToXmlStream(std::string& xmlStr)
				{
					tinyxml2::XMLDocument doc;
					tinyxml2::XMLElement* element = doc.NewElement("UdxDeclaration");
					doc.LinkEndChild(element);
					element->SetAttribute("name", this->getName());
					element->SetAttribute("description", this->getDescription()->getNodeDescription());

					tinyxml2::XMLElement* rootEle = doc.NewElement("UdxNode");
					element->LinkEndChild(rootEle);

					int count = this->getChildNodeCount();
					for (int iNode=0; iNode<count; iNode++)
					{
						IUdxNodeSchema* tempNode = this->getChildNode(iNode);
						FormatXDO(tempNode, rootEle, element);
					}

					tinyxml2::XMLPrinter printer;
					doc.Print(&printer);
					xmlStr = printer.CStr();

					return true;
				}

				virtual bool LoadFromXmlElement(const void* xmlElement)
				{
					tinyxml2::XMLElement* rootEle = (tinyxml2::XMLElement*)xmlElement;
					std::string dataset_name = rootEle->Attribute("name");
					std::string dataset_description = rootEle->Attribute("description");
					this->mName = dataset_name;
					this->getDescription()->modifyNodeDescription(dataset_description.c_str());

					rootEle = rootEle->FirstChildElement();

					for (tinyxml2::XMLElement* ele=rootEle->FirstChildElement(); ele; ele=ele->NextSiblingElement())
					{
						ParseXDO((CUdxNodeSchema*)this, ele);
					}

					tinyxml2::XMLElement* semanticEle = rootEle->FirstChildElement("SemanticAttachment");
					if (semanticEle!=NULL)
					{
						for (tinyxml2::XMLElement* childEle = semanticEle->FirstChildElement(); childEle; childEle = childEle->NextSiblingElement())
						{
							std::string ele_name = childEle->Name();
							if (ele_name=="Concepts")
							{
								for (tinyxml2::XMLElement* infoEle = childEle->FirstChildElement(); infoEle; infoEle = infoEle->NextSiblingElement())
								{
									std::string nodeId = infoEle->Attribute("nodeId");
									std::string resourceId = infoEle->Attribute("conceptId");
									IUdxNodeSchema* dataNode = getNodeWithId(nodeId, (CUdxNodeSchema*)this);
									if (dataNode)
										dataNode->getDescription()->modifyConceptTag(resourceId.c_str());
								}
							}
							else if (ele_name == "SpatialRefs")
							{
								for (tinyxml2::XMLElement* infoEle = childEle->FirstChildElement(); infoEle; infoEle = infoEle->NextSiblingElement())
								{
									std::string nodeId = infoEle->Attribute("nodeId");
									std::string resourceId = infoEle->Attribute("spatialRefId");
									IUdxNodeSchema* dataNode = getNodeWithId(nodeId, (CUdxNodeSchema*)this);
									if (dataNode)
										dataNode->getDescription()->modifySpatialReferenceTag(resourceId.c_str());
								}
							}
							else if (ele_name == "Units")
							{
								for (tinyxml2::XMLElement* infoEle = childEle->FirstChildElement(); infoEle; infoEle = infoEle->NextSiblingElement())
								{
									std::string nodeId = infoEle->Attribute("nodeId");
									std::string resourceId = infoEle->Attribute("unitId");
									IUdxNodeSchema* dataNode = getNodeWithId(nodeId, (CUdxNodeSchema*)this);
									if (dataNode)
										dataNode->getDescription()->modifyUnitTag(resourceId.c_str());
								}
							}
							else if (ele_name == "DataTemplates")
							{
								for (tinyxml2::XMLElement* infoEle = childEle->FirstChildElement(); infoEle; infoEle = infoEle->NextSiblingElement())
								{
									std::string nodeId = infoEle->Attribute("nodeId");
									std::string resourceId = infoEle->Attribute("dataTemplateId");
									IUdxNodeSchema* dataNode = getNodeWithId(nodeId, (CUdxNodeSchema*)this);
									if (dataNode)
										dataNode->getDescription()->modifyDataTemplateTag(resourceId.c_str());
								}
							}
						}
					}

					return true;
				}

				virtual bool FormatToXmlElement(void* xmlElement)
				{
					tinyxml2::XMLElement* externalElement = (tinyxml2::XMLElement*)xmlElement;
					tinyxml2::XMLDocument* doc = externalElement->GetDocument();
					tinyxml2::XMLElement* element = doc->NewElement("UdxDeclaration");
					externalElement->LinkEndChild(element);
					element->SetAttribute("name", this->getName());
					element->SetAttribute("description", this->getDescription()->getNodeDescription());

					tinyxml2::XMLElement* rootEle = doc->NewElement("UdxNode");
					element->LinkEndChild(rootEle);

					int count = this->getChildNodeCount();
					for (int iNode=0; iNode<count; iNode++)
					{
						IUdxNodeSchema* tempNode = this->getChildNode(iNode);
						FormatXDO(tempNode, rootEle, element);
					}

					return true;
				}

			};
		}
	}
}

#endif